using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.ServiceModel;
using System.Xml;

namespace Spike.ContractBuilder
{
    static internal class ContractHelper
    {
        static Random r = new Random();

        /// <summary>
        /// Determines the namespace associated with the method
        /// </summary>
        /// <param name="contractType"></param>
        /// <returns></returns>
        internal static string GetNamespace(Type contractType)
        {
            string ns = string.Empty;
            object[] attributes = contractType.GetCustomAttributes(typeof(ServiceContractAttribute), true);
            if (attributes.Length == 1)
            {
                ns = ((ServiceContractAttribute)attributes[0]).Namespace;
            }
            return ns;
        }
        internal static string GenerateSampleXml(ParameterInfo info)
        {
            if (info.ParameterType.IsClass)
            {
                object parameter = null;

                if (info.ParameterType.FullName == "System.String" ||
                    (info.ParameterType.Attributes & TypeAttributes.Abstract) == TypeAttributes.Abstract)
                    return info.Name ?? info.ParameterType.FullName;
                else
                {
                    parameter = CreateFullInstance(info.ParameterType);

                    Type ser = typeof(SerializationHelper<>).MakeGenericType(info.ParameterType);
                    string s = (string)ser.InvokeMember("SerializeServiceObject", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, new object[] { parameter });

                    XmlDocument doc = new XmlDocument();
                    doc.LoadXml(s);

                    return "\r\n" + doc.DocumentElement.InnerXml;
                }
            }
            else
            {
                object o = GetDefaultValue(info.Name, info.ParameterType);
                return o.ToString();
            }
        }
        internal static object CreateFullInstance(Type type)
        {
            object parameter = type.IsArray ? Activator.CreateInstance(type, new object[] { 1 }) : Activator.CreateInstance(type);

            if (type.IsArray)
            {
                // TODO: figure this out
            }
            else
            {
                foreach (FieldInfo field in type.GetFields())
                {
                    if (field.IsLiteral != true && field.IsPublic == true)
                    {
                        object value = GetDefaultValue(field.Name, field.FieldType);
                        field.SetValue(parameter, value);
                    }
                }
                foreach (PropertyInfo property in type.GetProperties())
                {
                    object value = GetDefaultValue(property.Name, property.PropertyType);
                    property.SetValue(parameter, value, null);
                }
            }
            return parameter;
        }
        internal static object GetDefaultValue(string name, Type type)
        {
            object value = null;
            switch (type.FullName)
            {
                case "System.Boolean":
                    return false;
                case "System.Guid":
                    return Guid.NewGuid();
                case "System.Void":
                    return type.FullName;
                case "System.Single":
                case "System.Double":
                case "System.Int16":
                case "System.Int32":
                case "System.Int64":
                    value = 0;
                    break;
                case "System.DateTime":
                    value = DateTime.Now;
                    break;
                case "System.String":
                    value = name;
                    break;
                case "System.Runtime.Serialization.ExtensionDataObject":
                    value = null;
                    break;
                default:
                    if (type.IsEnum)
                    {
                        value = Enum.GetValues(type).GetValue(0);
                    }
                    else if (type.IsClass)
                    {
                        value = CreateFullInstance(type);
                    }
                    else if (type.FullName.StartsWith("System.Nullable"))
                    {
                        // TODO: determine the type
                        return null;
                    }
                    else
                    {
                        throw new Exception("Not Supported Type: " + name);
                    }
                    break;
            }
            return value;
        }
        internal static object GetRandomObject(Type type)
        {
            // type code
            switch (type.FullName)
            {
                case "System.Void":
                    return null;
                case "System.Boolean":
                    return r.Next(0, 1) == 1;
                case "System.Single":
                case "System.Double":
                case "System.Int16":
                case "System.Int32":
                case "System.Int64":
                    return r.Next(1000);
                case "System.DateTime":
                    return DateTime.Now.AddMinutes(r.Next(400000) - 200000);
                case "System.String":
                    return "Stub returned a value of " + r.Next(1000).ToString() + ".";
            }

            // should now be a reference type or array

            // ending code
            if (type.IsArray == false)
            {
                return type.GetConstructor(new Type[] { }).Invoke(null);
            }
            else
            {
                // TODO: a better array than this...
                return Array.CreateInstance(type.GetElementType(), 0);
            }
        }

        internal static object DeserializeObject(Type type, string content)
        {
            return DeserializeObject(type, content, null);
        }
        internal static object DeserializeObject(Type type, string content, Type[] extraTypes)
        {
            if (typeof(Exception).IsAssignableFrom(type))
            {
                ConstructorInfo constructor = type.GetConstructor(new Type[] { typeof(string) });
                throw (Exception)constructor.Invoke(new object[] { content });
            }
            if (type.Name == "Void")
            {
                return null;
            }
            if(type==typeof(string))
            {
                return content;
            }
            if (type.IsEnum)
            {
                return Enum.Parse(type, content);
            }
            if (type.IsClass)
            {
                Type ser = typeof(SerializationHelper<>).MakeGenericType(type);
                if(extraTypes==null)
                    return ser.InvokeMember("Deserialize", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, new object[] { content});
                else
                    return ser.InvokeMember("Deserialize", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, new object[] { content, extraTypes });
            }
            else
            {
                MethodInfo parse = type.GetMethod("Parse", new Type[] { typeof(string) });

                if (parse != null)
                {
                    return parse.Invoke(null, new object[] { content });
                }
                else
                    throw new Exception("Type is not supported: " + type.FullName);
            }
        }
    }
}
